using System.Collections.Generic;
using System.IO;

namespace Engine.FileFormats3D.C11Undercover
{
    public class GeoFile
    {
        #region Internal Fields

        internal C11VertexBlock[] vertexBlocks;

        #endregion Internal Fields

        #region Private Fields

        private List<ushort> allIndices;

        private int numberOfUsedBlocks = 0;

        private UnknownData[] unknownData;

        #endregion Private Fields

        #region Public Constructors

        public GeoFile()
        {
            this.vertexBlocks = new C11VertexBlock[64];
            this.vertexBlocks[0] = new C11VertexBlock();
            this.allIndices = new List<ushort>(128000 * 3);
        }

        #endregion Public Constructors

        #region Public Properties

        public List<ushort> AllIndices
        {
            get { return this.allIndices; }
            set { this.allIndices = value; }
        }

        public int NumberOfUsedBlocks
        {
            get { return this.numberOfUsedBlocks; }
            set { this.numberOfUsedBlocks = value; }
        }

        #endregion Public Properties

        #region Internal Properties

        internal C11VertexBlock[] VertexBlocks
        {
            get { return this.vertexBlocks; }
            set { this.vertexBlocks = value; }
        }

        #endregion Internal Properties

        #region Public Methods

        public static GeoFile FromFile(string filename, QadFile qadFile)
        {
            GeoFile geoFile = new GeoFile();
            FileStream fs = new FileStream(filename, FileMode.Open);
            BinaryReader br = new BinaryReader(fs);

            char[] identy = br.ReadChars(4); // Alpen , Autobahn, Rennstrecke, Stadium
            short index1 = br.ReadInt16(); // 4, 4, 4, 4
            short index2 = br.ReadInt16(); // 1, 1, 1, 1
            short index3 = br.ReadInt16(); // 3, 3, 3, 3
            short index4 = br.ReadInt16();
            short index5 = br.ReadInt16(); // 64, 64, 64
            short index6 = br.ReadInt16();
            uint numberOfIndices = br.ReadUInt32();
            //short index7 = br.ReadInt16(); // 28366, 9336, 30411, 11494
            //short index8 = br.ReadInt16(); // 74, 57, 21, 20
            short index9 = br.ReadInt16();
            short index10 = br.ReadInt16();
            short index11 = br.ReadInt16();
            short index12 = br.ReadInt16();
            short index13 = br.ReadInt16();
            short index14 = br.ReadInt16();

            int[] numberOfVerticesPerBlock = new int[64];
            int numberOfVerticesComplete = 0;
            for (int i = 0; i < numberOfVerticesPerBlock.Length; i++)
            {
                numberOfVerticesPerBlock[i] = br.ReadInt32();
                numberOfVerticesComplete += numberOfVerticesPerBlock[i];
            }

            // Read vertices.
            geoFile.VertexBlocks = new C11VertexBlock[numberOfVerticesPerBlock.Length];
            for (int i = 0; i < numberOfVerticesPerBlock.Length; i++)
            {
                int numberOfVertices = numberOfVerticesPerBlock[i];
                if (numberOfVertices > 0)
                {
                    geoFile.VertexBlocks[i] = C11VertexBlock.FromStream(br, numberOfVertices);
                }
                else
                    break; // No further Vertexblock.

                // Ends at: 67301608 (Alpen)

                { // Just for debugging.
                    StreamWriter sw = new StreamWriter("TextureCoordinatesOfVertices.txt", false);
                    foreach (var item in geoFile.vertexBlocks[i].Vertices)
                    {
                        sw.WriteLine(item.TextureU1 + "\t" + item.TextureV1 + "\t" + item.TextureV2 + "\t" + item.TextureU2 + "\t");
                    }
                    sw.Close();
                }
            }

            long startPosition = br.BaseStream.Position;

            long sizeOfUnknownBlock = (br.BaseStream.Length - qadFile.NumberOfPolygons * 6) - br.BaseStream.Position;
            //byte[] unknownBlock = br.ReadBytes((int)sizeOfUnknownBlock);

            geoFile.unknownData = new UnknownData[numberOfVerticesComplete * 2];

            for (int i = 0; i < geoFile.unknownData.Length; i++)
            {
                geoFile.unknownData[i] = new UnknownData(br);
            }
            // System.Windows.Forms.MessageBox.Show("Number of Unknown Block Data: " + geoFile.unknownData.Length);

            {
                FileStream fw = new FileStream(filename + "_unknownBlock.bin", FileMode.Create);
                BinaryWriter bw = new BinaryWriter(fw);
                foreach (var item in geoFile.unknownData)
                {
                    item.Save(bw);
                }
                bw.Close();
                fw.Close();
            }

            br.BaseStream.Position = br.BaseStream.Length - qadFile.NumberOfPolygons * 6; // for each polygon 3 points * 2 bytes.

            //Read the indices
            geoFile.AllIndices = new List<ushort>((int)qadFile.NumberOfPolygons * 3);
            for (int i = 0; i < numberOfIndices; i++)
            {
                geoFile.AllIndices.Add(br.ReadUInt16());
            }

            br.Close();
            fs.Close();
            return geoFile;
        }

        public void ToFile(string filename, QadFile qadFile)
        {
            FileStream fs = new FileStream(filename, FileMode.Create);
            BinaryWriter bw = new BinaryWriter(fs);

            bw.Write(new char[] { 'M', 'O', 'E', 'G' });

            bw.Write((short)4);
            bw.Write((short)1);
            bw.Write((short)3);
            bw.Write((short)0);
            bw.Write((short)64);
            bw.Write((short)0);
            bw.Write((uint)this.allIndices.Count);
            //bw.Write((short)0);
            //bw.Write((short)0);
            bw.Write((short)0);
            bw.Write((short)0);
            bw.Write((short)0);
            bw.Write((short)0);
            bw.Write((short)0);
            bw.Write((short)0);

            // Write the number of each block
            for (int i = 0; i < 64; i++)
            {
                if (this.vertexBlocks[i] != null)
                    bw.Write((int)this.vertexBlocks[i].EngineVertices.Count);
                else
                    bw.Write((int)0);
            }

            // Write the vertices
            int numberOfVertices = 0;
            for (int i = 0; i < 64; i++)
            {
                if (this.vertexBlocks[i] != null)
                {
                    if (this.vertexBlocks[i].Vertices == null)
                        this.vertexBlocks[i].UpdateC11Vertices();

                    numberOfVertices += this.vertexBlocks[i].Vertices.Length;
                    for (int j = 0; j < this.vertexBlocks[i].Vertices.Length; j++)
                    {
                        this.vertexBlocks[i].Vertices[j].ToStream(bw);
                    }
                }
            }

            // if no data exist, create it.
            if (this.unknownData == null)
            {
                this.unknownData = new UnknownData[this.GetNumberOfVertices() * 2];
                for (int i = 0; i < this.unknownData.Length; i++)
                {
                    this.unknownData[i] = new UnknownData();
                }
            }

            // Unknown Data
            foreach (var item in this.unknownData)
            {
                item.Save(bw);
            }

            // Write the indices
            for (int i = 0; i < this.AllIndices.Count; i++)
            {
                bw.Write((ushort)this.AllIndices[i]);
            }

            bw.Close();
            fs.Close();
        }

        #endregion Public Methods

        #region Internal Methods

        internal void AddNewVertexblock()
        {
            this.vertexBlocks[this.numberOfUsedBlocks + 1] = new C11VertexBlock();
            this.numberOfUsedBlocks++;
        }

        internal int GetNumberOfVertices()
        {
            int numberOfVertices = 0;
            foreach (var item in this.vertexBlocks)
            {
                if (item == null)
                    break;

                numberOfVertices += item.EngineVertices.Count;
            }
            return numberOfVertices;
        }

        #endregion Internal Methods

        #region Private Classes

        /// <summary>
        /// Tangents or Binormals or something like that.
        /// </summary>
        private class UnknownData
        {
            #region Private Fields

            private readonly short[] data;

            #endregion Private Fields

            #region Public Constructors

            public UnknownData()
            {
                this.data = new short[4];
                // data[3] = 15360;
            }

            public UnknownData(BinaryReader br)
                : this()
            {
                this.data[0] = br.ReadInt16();
                this.data[1] = br.ReadInt16();
                this.data[2] = br.ReadInt16();
                this.data[3] = br.ReadInt16();
            }

            #endregion Public Constructors

            #region Public Methods

            public void Save(BinaryWriter bw)
            {
                //this.data[0] = 0;
                //this.data[1] = 0xFF;
                //this.data[2] = 0;
                //this.data[3] = 0;

                bw.Write(this.data[0]);
                bw.Write(this.data[1]);
                bw.Write(this.data[2]);
                bw.Write(this.data[3]);
            }

            #endregion Public Methods
        }

        #endregion Private Classes
    }
}